package com.smx.steppingstone.visitors;	

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.smx.steppingstone.util.ReflectUtil;
public class XMLVisitor extends  AbstractVisitor {
	private StringBuffer buffer;
	public XMLVisitor(){
		buffer=new StringBuffer();
	}	

	public XMLVisitor(int maxdepth){
		super(maxdepth);
		buffer=new StringBuffer();
	}	
	@Override
	public void visitCollection(Collection<?> o, Field f) {		
		String clas=f==null?"":f.getName();
		c("<collection type=\""+o.getClass().getName()+"\" field=\""+clas+ "\" >", depth);
		for(Object item:o){			
			visit(item);			
		}
		c("</collection>", depth);
	}

	@Override
	public void visitMap(Map<?, ?> o, Field f) {
		String clas=f==null?"":f.getName();
		c("<collection type=\""+o.getClass().getName()+"\" field=\""+clas+ "\" >", depth);
		c("<object name=\""+ o.getClass().getSimpleName()+"\" class=\""+ o.getClass().getCanonicalName()+"\"> ",depth);	
		c("<set>", depth);
		for(Object item:o.entrySet()){			
			c("<entry>", depth);
			c("<key>", depth);
			visit(((Entry<?,?>)item).getKey(), f);
			c("</key>", depth);
			c("<value>", depth);	
			visit(((Entry<?,?>)item).getValue(),f );
			c("</value>", depth);
			c("</entry>", depth);
		}
		c("</set>", depth);
		c("</object>", depth);
		c("</collection>", depth);
	}
	@Override
	public void visitArray(Object[] o, Field f) {
		String clas=f==null?"":f.getName();
		c("<array type=\""+o.getClass().getName()+"\" field=\""+clas+ "\" >", depth);
		c("<object name=\""+ o.getClass().getSimpleName()+"\" class=\""+ o.getClass().getCanonicalName()+"\"> ",depth);	
		c("<row>", depth);
		for(int i=0;i<o.length;i++){
			Object item=o[i];
			c("<entry>", depth);
			c("<index value=\""+i+"\">", depth);
			visit(item, f);
			c("</index>", depth);
			c("<value>", depth);	
			visit(item,f );
			c("</value>", depth);
			c("</entry>", depth);
		}
		c("</row>", depth);
		c("</object>", depth);
		c("</array>", depth);
	}

	@Override
	public void visitFloat(Float o, Field f) {		
		appendField("Float", (null != f)?f.getName():"anonymous", o);
	}

	public void visitBoolean(Boolean o, Field f) {		
		appendField("Boolean", (null != f)?f.getName():"anonymous", o);
	}

	@Override
	public void visitInteger(Integer o, Field f) {
		appendField("Integer", (null != f)?f.getName():"anonymous", o);
	}
	@Override
	public void visitDate(Date o, Field f) {
		appendField("Date", (null != f)?f.getName():"anonymous", o);
	}
	@Override
	public void visitMethod(Object o, Method m) {
		c("<method signature=\""+m+"\">", depth);
		depth++;
		//FIXME:Need to figure out a way to detect circular dependencies.
		try{
			visit(ReflectUtil.getMethodValue(o, m));
		}catch(Exception ex){
			//Suppressed Ex
		}
		depth--;
		c("</method>", depth);
	}

	private void appendField(String type, String name, Object value){
		c("<field type=\""+type+"\" name=\""+name+"\" value=\""+value+"\" />" , depth);
	}
	@Override
	public void visitObject(Object o, Field f) {
		if(depth>maxdepth){
			return;
		}
		++depth;
		c((null!=f)?f.getName():""+"<object name=\""+ o.getClass().getSimpleName()+"\" class=\""+ o.getClass().getCanonicalName()+"\"> ",depth);
		try {
			depth++;
			List<Field> fields=ReflectUtil.getAccesibleFields(o);												
			c("<fields>", depth);
			depth++;
			for(Field field:fields){	
				try{
					Object out=field.get(o);
					visit(out, field);
				}catch(IllegalAccessException iax){
					//iax.printStackTrace();
					field.setAccessible(true);
					try{
						Object out=field.get(o);
						visit(out, field);
					}catch(IllegalAccessException iax2){						
						//iax2.printStackTrace();
					}
				}
			}
			depth--;
			c("</fields>", depth);	
			c("<methods>", depth);
			depth++;

			for(Method method : ReflectUtil.getGetterOrIsMethods(o)){	
				visit(o, method);
			}
			depth--;
			c("</methods>", depth);
			depth--;
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} 
		c((null!=f)?f.getName():""+"</object>",depth);
		depth--;		
	}

	@Override
	public void visitString(String o, Field f) {
		appendField("String", (null != f)?f.getName():"anonymous", o);
	}

	public void c(Object o, int depth){
		buffer.append("\n ");		
		for(int i=1;i<depth;i++)buffer.append("   ");
		buffer.append(o);	
	}

	public StringBuffer getBuffer() {				
		StringBuffer tmp=new StringBuffer();
		tmp.append("<instance>");
		tmp.append(buffer);
		tmp.append("\n</instance>");
		return tmp;
	}
	public void setBuffer(StringBuffer buffer) {
		this.buffer = buffer;
	}

}
